using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Diagnostics;

namespace XNASubdivision
{
    struct Quad
    {
        public int vertInd0;     // since uint[] is a pointer, we cannot use it in struct
        public int vertInd1;
        public int vertInd2;
        public int vertInd3;

        public int edgeInd0;     // since uint[] is a pointer, we cannot use it in struct
        public int edgeInd1;
        public int edgeInd2;
        public int edgeInd3;

        public Quad(int p_0, int p_1, int p_2, int p_3)
        {
            vertInd0 = p_0;
            vertInd1 = p_1;
            vertInd2 = p_2;
            vertInd3 = p_3;

            edgeInd0 = -1;
            edgeInd1 = -1;
            edgeInd2 = -1;
            edgeInd3 = -1;
        }
    }

    struct Int4
    {
        public int x;     // since uint[] is a pointer, we cannot use it in struct
        public int y;
        public int z;
        public int w;

        public Int4(int p_x, int p_y, int p_z, int p_w)
        {
            x = p_x;
            y = p_y;
            z = p_z;
            w = p_w;
        }
    }

    struct Int2
    {
        public int x;     // since uint[] is a pointer, we cannot use it in struct
        public int y;

        public Int2(int p_x, int p_y)
        {
            x = p_x;
            y = p_y;
        }
    }
   

    struct Vertex
    {
        public Vector3 pos;
        public Vector3 normal;
        public Vector2 textureUV;
    }

    struct Edge
    {
        public int vertInd0;
        public int vertInd1;

        public Edge(int p_ind0, int p_ind1)
        {
            vertInd0 = p_ind0;
            vertInd1 = p_ind1;
        }
    }



    public class QuadMesh
    {
        Vector3[] m_vertices;
        Vector3[] m_normals;
        Vector2[] m_textureUVs;

        Quad[] m_quads;
        Int4[] m_quadTextureInd;
        Int4[] m_quadNormalInd;

      

        List<Edge> m_edges = new List<Edge>();  // not necessary to store vertices
        

        public QuadMesh()
        {
        }

        public QuadMesh(string p_fullPathObj)
        {
            ModelLoader loader = new ModelLoader();
            loader.ReadFile(p_fullPathObj);

            Debug.Assert(loader.vertices.Length % 3 == 0);
            Debug.Assert(loader.faces.Length % (4 * 3) == 0);
            int vertexCount = loader.vertices.Length / 3;
            int normalCount = loader.normals.Length / 3;
            int textureCount = loader.textures.Length / 2;
            int quadCount = loader.faces.Length / (4 * 3);


            m_vertices = new Vector3[vertexCount];
            
            
            for (int i = 0; i < vertexCount; i++)
            {
                m_vertices[i] = new Vector3(loader.vertices[3 * i], loader.vertices[3 * i + 1], -1.0f * loader.vertices[3 * i + 2] + 5);
            }

            m_normals = new Vector3[normalCount];
            for (int i = 0; i < normalCount; i++)
            {
                m_normals[i] = new Vector3(loader.normals[3 * i], loader.normals[3 * i + 1], -1.0f * loader.normals[3 * i + 2]);
            }

            m_textureUVs = new Vector2[textureCount];
            for (int i = 0; i < textureCount; i++)
            {
                m_textureUVs[i] = new Vector2(loader.textures[2 * i], -1.0f * (loader.textures[2 * i + 1] - 1.0f));
            }
            
            


            m_quads = new Quad[quadCount];
            m_quadTextureInd = new Int4[quadCount];
            m_quadNormalInd = new Int4[quadCount];
            for (int i = 0; i < quadCount; i++)
            {
                m_quads[i].vertInd0 = loader.faces[12 * i] - 1;     // start from index 0, not from index 1
                m_quads[i].vertInd1 = loader.faces[12 * i + 3] - 1;
                m_quads[i].vertInd2 = loader.faces[12 * i + 6] - 1;
                m_quads[i].vertInd3 = loader.faces[12 * i + 9] - 1;

                m_quadTextureInd[i].x = loader.faces[12 * i + 1] - 1;       // f vertexInd/textureInd/normalInd is the order in the OBJ file
                m_quadTextureInd[i].y = loader.faces[12 * i + 4] - 1;
                m_quadTextureInd[i].z = loader.faces[12 * i + 7] - 1;
                m_quadTextureInd[i].w = loader.faces[12 * i + 10] - 1;

                m_quadNormalInd[i].x = loader.faces[12 * i + 2] - 1;       // f vertexInd/textureInd/normalInd is the order in the OBJ file
                m_quadNormalInd[i].y = loader.faces[12 * i + 5] - 1;
                m_quadNormalInd[i].z = loader.faces[12 * i + 8] - 1;
                m_quadNormalInd[i].w = loader.faces[12 * i + 11] - 1; 
            }

        }

        public Vector3[] Vertices
        {
            get { return m_vertices; }
            set { m_vertices = value; }
        }

        public Vector3[] Normals
        {
            get { return m_normals; }
            set { m_normals = value; }
        }

        public Vector2[] TextureUVs
        {
            get { return m_textureUVs; }
            set { m_textureUVs = value; }
        }

        internal Quad[] Quads
        {
            get { return m_quads; }
            set { m_quads = value; }
        }

        internal Int4[] QuadTextureInd
        {
            get { return m_quadTextureInd; }
            set { m_quadTextureInd = value; }
        }

        internal Int4[] QuadNormalInd
        {
            get { return m_quadNormalInd; }
            set { m_quadNormalInd = value; }
        }

        internal List<Edge> Edges
        {
            get { return m_edges; }
        }

       

        public int VertexCount
        {
            get { return m_vertices.Length; }
        }


        public int QuadCount
        {
            get { return m_quads.Length; }
        }




        internal Vertex CreateQuadCenter(int p_i)
        {
            return CreateAverage(m_quads[p_i].vertInd0, m_quads[p_i].vertInd1, m_quads[p_i].vertInd2, m_quads[p_i].vertInd3);
        }

        internal Vertex CreateAverage(int p_0, int p_1, int p_2, int p_3)
        {
            Vertex vertex = new Vertex();

            vertex.pos = m_vertices[p_0] + m_vertices[p_1] + m_vertices[p_2] + m_vertices[p_3];
            vertex.pos = vertex.pos * 0.25f;

            vertex.textureUV = m_textureUVs[p_0] + m_textureUVs[p_1] + m_textureUVs[p_2] + m_textureUVs[p_3];
            vertex.textureUV = vertex.textureUV * 0.25f;

            vertex.normal = m_normals[p_0] + m_normals[p_1] + m_normals[p_2] + m_normals[p_3];
            vertex.normal.Normalize();

            return vertex;
        }

        internal Vertex CreateAverage(int p_0, int p_1, int p_2)
        {
            Vertex vertex = new Vertex();

            vertex.pos = m_vertices[p_0] + m_vertices[p_1] + m_vertices[p_2];
            vertex.pos = vertex.pos / 3;

            vertex.textureUV = m_textureUVs[p_0] + m_textureUVs[p_1] + m_textureUVs[p_2];
            vertex.textureUV = vertex.textureUV / 3;

            vertex.normal = m_normals[p_0] + m_normals[p_1] + m_normals[p_2];
            vertex.normal.Normalize();

            return vertex;
        }

        internal List<int> FindQuadsForEdge(int p_edgeIndex)
        {
            List<int> quadsIndices = new List<int>();
            for (int i = 0; i < m_quads.Length; i++)
            {
                if (m_quads[i].edgeInd0 == p_edgeIndex || m_quads[i].edgeInd1 == p_edgeIndex
                            || m_quads[i].edgeInd2 == p_edgeIndex || m_quads[i].edgeInd3 == p_edgeIndex)
                    quadsIndices.Add(i);
            }

            return quadsIndices;
        }

        internal List<int> FindQuadsForVertex(int p_vertIndex)
        {
            List<int> neigFaces = new List<int>();
            for (int i = 0; i < m_quads.Length; i++)
            {
                if (m_quads[i].vertInd0 == p_vertIndex || m_quads[i].vertInd1 == p_vertIndex
                    || m_quads[i].vertInd2 == p_vertIndex || m_quads[i].vertInd3 == p_vertIndex)
                    neigFaces.Add(i);

            }

            return neigFaces;
        }

        //internal int EdgesCountForVertex(int p_vertex)
        //{
        //    int nEdgeCount = 0;
        //    for (int i = 0; i < m_edges.Count; i++)
        //    {
        //        if (m_edges[i].vertInd0 == p_vertex || m_edges[i].vertInd1 == p_vertex)
        //            nEdgeCount++;
        //    }
        //    return nEdgeCount;
        //}

        internal List<int> EdgesForVertex(int p_vertex)
        {
            List<int> neigEdges = new List<int>();
            for (int i = 0; i < m_edges.Count; i++)
            {
                if (m_edges[i].vertInd0 == p_vertex || m_edges[i].vertInd1 == p_vertex)
                    neigEdges.Add(i);
            }
            return neigEdges;
        }


        internal Vertex CreateEdgeMiddle(int p_i)
        {
            Vertex vertex = new Vertex();

            vertex.pos = 0.5f * (m_vertices[m_edges[p_i].vertInd0] + m_vertices[m_edges[p_i].vertInd1]);
            vertex.textureUV = 0.5f * (m_textureUVs[m_edges[p_i].vertInd0] + m_textureUVs[m_edges[p_i].vertInd1]);
            vertex.normal = m_normals[m_edges[p_i].vertInd0] + m_normals[m_edges[p_i].vertInd1];
            vertex.normal.Normalize();

            return vertex;
        }

     

        
      
    }
}
